// Copyright (c) Microsoft Corporation. All rights reserved.

using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Reflection;
using System.Runtime.Serialization;
using System.Threading;
using System.Text;
using Microsoft.Win32.SafeHandles;

namespace PipeServer
{
	class Program
	{
		#region Native methods

		[Flags]
		private enum PipeMode
		{
			Wait = 0x00000000,
			NoWait = 0x00000001,
			ReadModeByte = 0x00000000,
			ReadModeMessage = 0x00000002,
			TypeByte = 0x00000000,
			TypeMessage = 0x00000004,
		}

		private enum PipeAccess
		{
			Inbound = 0x00000001,
			Outbound = 0x00000002,
			Duplex = 0x00000003
		}

		private class Error
		{
			public const int PipeConnected = 535;
		}

		private class NativeMethods
		{
			[DllImport("kernel32.dll", SetLastError = true)]
			public static extern bool SetNamedPipeHandleState(
				SafeFileHandle hPipe, ref PipeMode mode,
				IntPtr maxCollectionCount, IntPtr collectDataTimeout);

			[DllImport("kernel32.dll", SetLastError = true)]
			public static extern bool WriteFile(
				SafeFileHandle hFile, byte[] buffer, int numberOfBytesToWrite,
				out int numberOfBytesWritten, IntPtr overlapped);

			[DllImport("kernel32.dll", SetLastError = true)]
			public static extern SafeFileHandle CreateNamedPipe(
				string name, PipeAccess access, PipeMode mode,
				int maxInstances, int outBufferSize, int inBufferSize,
				int defaultTimeout, IntPtr securityDescriptor);

			[DllImport("kernel32.dll", SetLastError = true)]
			public static extern bool ConnectNamedPipe(SafeFileHandle hPipe, IntPtr overlapped);
		}

		#endregion

		static void Main(string[] args)
		{
			const int maxMessageLength = 32768;
			
			// Create a pipe to communicate with the remote thread
			SafeFileHandle hPipe = null;
			
			try
			{
			    // Signal that the server has started
                try
                {
                    EventWaitHandle serverStartedEvent = EventWaitHandle.OpenExisting("CodeRunner.Test.ServerStarted.Event");
                
                    serverStartedEvent.Set();
                }
                catch (WaitHandleCannotBeOpenedException) {}
			
				// Create a pipe to communicate with the remote thread.  This must be done first so that the 
				// pipe is ready when the remote thread starts
				hPipe = NativeMethods.CreateNamedPipe(
					@"\\.\pipe\coderunner$", PipeAccess.Duplex,
					PipeMode.TypeMessage | PipeMode.ReadModeMessage | PipeMode.Wait,
					255, maxMessageLength * Marshal.SizeOf(typeof(char)), maxMessageLength * Marshal.SizeOf(typeof(char)),
					0, IntPtr.Zero);

				Console.WriteLine("Waiting for pipe client...");

				bool connected = NativeMethods.ConnectNamedPipe(hPipe, IntPtr.Zero) ?
					true : (Marshal.GetLastWin32Error() == Error.PipeConnected);

				if (!connected)
				{
					Console.WriteLine("ERROR: Client did not connect.");
					return;
				}

				Console.WriteLine("Pipe client connected");

				// Send the message to the client
				string msg = String.Format("set\r\n{0}={1}\0", "CR_TEST_VARIABLE", "abc");
				byte[] bytes = Encoding.Unicode.GetBytes(msg);
				int bytesWritten;

				Console.WriteLine("Setting CR_TEST_VARIABLE");
				bool ok = NativeMethods.WriteFile(hPipe, bytes, bytes.Length, out bytesWritten, IntPtr.Zero);

				bytes = Encoding.Unicode.GetBytes("terminate\0");

				Console.WriteLine("Terminating pipe server");
				ok = NativeMethods.WriteFile(hPipe, bytes, bytes.Length, out bytesWritten, IntPtr.Zero);

				Console.WriteLine("Pipe server exiting");
			}
			finally
			{
				// Now we are done with the pipe
				if (hPipe != null)
					hPipe.Close();
			}
		}
	}
}
